<!DOCTYPE HTML PUBLIC "-//ORA//DTD CD HTML 3.2//EN">
<HTML>
<HEAD>
<TITLE>[Chapter 11] 11.9 Creating custom components</TITLE>
<META NAME="author" CONTENT="Pat Niemeyer and Josh Peck">
<META NAME="date" CONTENT="Tue Jul 22 19:01:22 1997">
<META NAME="form" CONTENT="html">
<META NAME="metadata" CONTENT="dublincore.0.1">
<META NAME="objecttype" CONTENT="book part">
<META NAME="otheragent" CONTENT="gmat dbtohtml">
<META NAME="publisher" CONTENT="O'Reilly &amp; Associates, Inc.">
<META NAME="source" CONTENT="SGML">
<META NAME="subject" CONTENT="Java">
<META NAME="title" CONTENT="Exploring Java">
<META HTTP-EQUIV="Content-Script-Type" CONTENT="text/javascript">
</HEAD>
<body vlink="#551a8b" alink="#ff0000" text="#000000" bgcolor="#FFFFFF" link="#0000ee">

<DIV CLASS=htmlnav>
<H1><a href='index.htm'><IMG SRC="gifs/smbanner.gif"
     ALT="Exploring Java" border=0></a></H1>
<table width=515 border=0 cellpadding=0 cellspacing=0>
<tr>
<td width=172 align=left valign=top><A HREF="ch11_08.htm"><IMG SRC="gifs/txtpreva.gif" ALT="Previous" border=0></A></td>
<td width=171 align=center valign=top><B><FONT FACE="ARIEL,HELVETICA,HELV,SANSERIF" SIZE="-1">Chapter 11<br>Using and Creating GUI Components</FONT></B></TD>
<td width=172 align=right valign=top><A HREF="ch12_01.htm"><IMG SRC="gifs/txtnexta.gif" ALT="Next" border=0></A></td>
</tr>
</table>

&nbsp;
<hr align=left width=515>
</DIV>
<DIV CLASS=sect1>
<h2 CLASS=sect1><A CLASS="TITLE" NAME="ch11-SECT1-AUTOID.4">11.9 Creating custom components</A></h2>

<P CLASS=para>
In the previous sections, we've worked with many different user
interface objects, and made a lot of new classes that are sort of like
components. Our new classes do one particular thing well; a number of
them can be added to applets or other containers just like the
standard AWT components; and several of them are lightweight
components that use system resources efficiently because they don't
rely on a peer. 
<P CLASS=para>
But these new classes still aren't really components. 
If you think about it, all our classes have
been fairly self-contained; they know everything about what to
do, and don't rely on other parts of the program to do much
processing. Therefore, they are overly specialized. Our menu example
created a <tt CLASS=literal>DinnerFrame</tt> class that had a
menu of dinner options, but it included all the processing needed to
handle the user's selections. If we wanted to process the selections
differently, we'd have to modify the class. That's not what we want;
we'd like to separate registering the user's choices from processing
those choices. In contrast, true components don't do any processing.
They let the user take some action, and then inform some other part of
the program, which processes the action. 
<P CLASS=para>
So we need a way for our new classes to communicate with other parts
of the program. Since we want our new classes to be components, they
should communicate the way components communicate: that is, by
generating 
events and sending those events to listeners. So far, we've written a
lot of code that listened for events, but haven't seen any examples
that generated events.
<P CLASS=para>
Generating events sounds like it ought to be difficult, but it
isn't. You can either create new kinds of events, by subclassing
<tt CLASS=literal>AWTEvent</tt>, or use one of the standard event
types. In either case, you need to register listeners for your events,
and provide a means to deliver events to your listeners. If you are
using the standard events, AWT provides an
<tt CLASS=literal>AWTEventMulticaster</tt> class that handles
most of the machinery. We'll focus on that option in this section; at
the end, we'll make some comments on how you might manage events on
your own. 
<P CLASS=para>
The <tt CLASS=literal>AWTEventMulticaster</tt> is one of those
things that looks a lot more complicated than it is. It is confusing,
but most of the confusion occurs because it's hard to believe it's so
simple. Its job is to maintain a linked list of event listeners and
to propagate events to all the listeners on that linked list. So we can use
a multicaster to register (and unregister) event listeners and to
send any events we generate to all registered listeners. 
<P CLASS=para>
The best way to show you how to use the multicaster is through an
example. The following example creates a new component called
<tt CLASS=literal>PictureButton</tt>.
<tt CLASS=literal>PictureButton</tt> 
looks at least somewhat button-like and responds to mouse clicks
(<tt CLASS=literal>MOUSE_RELEASED</tt> events) by generating
action events. (<A HREF="ch11_06.htm#EXJ-CH-11-FIG-7">Figure 11.7</A> shows a
<tt CLASS=literal>PictureButton</tt> in both depressed and raised
modes.) The <tt CLASS=literal>PictureButtonApplet</tt> is passed 
the events in  
its <tt CLASS=literal>actionPerformed()</tt> method, just as with
any other button, and prints a message each time it's pressed.

<DIV CLASS=figure>
<h4 CLASS=figure><A CLASS="TITLE" NAME="EXJ-CH-11-FIG-10">Figure 11.10: The PictureButtonApplet</A></h4>


<p>
<img align=middle src="./figs/je1110.gif" alt="[Graphic: Figure 11-10]" width=503 height=193 border=0>

</DIV>

<DIV CLASS=screen>
<P>
<PRE>
import java.awt.*;
import java.awt.event.*;
public class PictureButtonApplet extends java.applet.Applet implements ActionListener { 
    Image image;
    public void init() {
        image = getImage( getClass().getResource(getParameter("image")) );
        PictureButton pictureButton = new PictureButton( image );
        add ( pictureButton );
        pictureButton.setActionCommand("Aaargh!");
        pictureButton.addActionListener( this );
    }
    
    public void actionPerformed( ActionEvent e ) {
        System.out.println( e );
    }
}
class PictureButton extends Component {
    private Image image;
    boolean pressed = false;
    ActionListener actionListener;
    String actionCommand;
    PictureButton(Image image) {
        this.image = image;
        MediaTracker mt = new MediaTracker(this);
        mt.addImage( image, 0 );
        try { mt.waitForAll(); } catch (InterruptedException e) { /* error */ };
        setSize( image.getWidth(null), image.getHeight(null) );
        enableEvents( AWTEvent.MOUSE_EVENT_MASK );
    }
    public void paint( Graphics g ) {
        g.setColor(Color.white);
        int width = getSize().width, height = getSize().height;
        int offset = pressed ? -2 : 0;  // fake depth
        g.drawImage( image, offset, offset, width, height, this );
        g.draw3DRect(0, 0, width-1, height-1, !pressed);
        g.draw3DRect(1, 1, width-3, height-3, !pressed);
    }
    public Dimension getPreferredSize() {
        return getSize();
    }
    public void processEvent( AWTEvent e ) {
        if ( e.getID() == MouseEvent.MOUSE_PRESSED ) {
            pressed = true;
            repaint();
        } else 
        if ( e.getID() == MouseEvent.MOUSE_RELEASED ) {
            pressed = false;
            repaint();
            fireEvent();
        }
        super.processEvent(e);
    }
    public void setActionCommand( String actionCommand ) {
        this.actionCommand = actionCommand;
    }
    public void addActionListener(ActionListener l) {
        actionListener = AWTEventMulticaster.add(actionListener, l);
    }
    public void removeActionListener(ActionListener l) {
        actionListener = AWTEventMulticaster.remove(actionListener, l);
    }
    private void fireEvent() {
        if (actionListener != null) {
            ActionEvent event = new ActionEvent( this, 
                                ActionEvent.ACTION_PERFORMED, actionCommand );
            actionListener.actionPerformed( event );
        }
    }
}
</PRE>
</DIV>

<P CLASS=para>
Before diving into the event multicaster, here are a few notes about
the applet and the <tt CLASS=literal>PictureButton</tt>. The
applet is an <tt CLASS=literal>ActionListener</tt> because it is
looking for events coming from the button. Therefore it registers
itself as a listener and contains an
<tt CLASS=literal>actionPerformed()</tt> method. The
<tt CLASS=literal>PictureButton</tt> doesn't have a label, so the
applet explicitly sets the button's action command by calling
<tt CLASS=literal>setActionCommand()</tt>. 
<P CLASS=para>
The button itself is concerned mostly with being pretty. It uses a
media tracker to make sure that the image has loaded before displaying
itself. The <tt CLASS=literal>paint()</tt> method, which we won't
discuss in detail, is devoted to making the button appear "pressed"
(i.e., recessed) when the mouse is pressed. The
<tt CLASS=literal>getPreferredSize()</tt> method lets layout
managers size the button appropriately. 
<P CLASS=para>
Now we'll start with the button's machinery. The button needs to
receive mouse events. It could register as a mouse listener, but in
this case, it seems more appropriate to override
<tt CLASS=literal>processEvent()</tt>. 
<tt CLASS=literal>processEvent()</tt> receives all incoming
events. It first checks whether we have a
<tt CLASS=literal>MOUSE_PRESSED</tt> event; if so, it tells the
button to repaint itself in its "pressed" mode. If the event is a
<tt CLASS=literal>MOUSE_RELEASED</tt> event, it tells the button
to paint itself in its "unpressed" mode and calls the private
<tt CLASS=literal>fireEvent()</tt> method, which sends an action
event to all listeners. Finally,
<tt CLASS=literal>processEvent()</tt> calls
<tt CLASS=literal>super.processEvent()</tt> to make sure normal
event processing occurs; this is a good practice whenever you override
a method that performs a significant task. 
<P CLASS=para>
However, <tt CLASS=literal>processEvent()</tt> doesn't receive
events if they aren't generated; and normally, events aren't generated
if there are no listeners. Therefore, the button's constructor calls
<tt CLASS=literal>enableEvents()</tt> with the argument
<tt CLASS=literal>MOUSE_EVENT_MASK</tt> to turn on mouse event
processing. 
<P CLASS=para>
Now we're ready to talk about how to generate events. The picture
button has <tt CLASS=literal>addActionListener()</tt> and
<tt CLASS=literal>removeActionListener()</tt> methods, for
registering listeners. These just call the static methods
<tt CLASS=literal>add()</tt> and
<tt CLASS=literal>remove()</tt> of the
<tt CLASS=literal>AWTEventMulticaster</tt> class. Here's the
<tt CLASS=literal>addActionListener()</tt> method:

<DIV CLASS=screen>
<P>
<PRE>
public void addActionListener(ActionListener l) {
    actionListener = AWTEventMulticaster.add(actionListener, l);
}
</PRE>
</DIV>

<P CLASS=para>
If you look back to see how the instance variable
<tt CLASS=literal>actionListener</tt> is declared, you'll see
that it is an <tt CLASS=literal>ActionListener</tt>.  No big
surprise--except that this code doesn't appear to make sense.  It's
saying "add an action listener to an action listener and store the
result back in the original action listener."

<P CLASS=para>
There are a couple of tricks here. First, an
<tt CLASS=literal>AWTEventMulticaster</tt> implements all of the
listener interfaces. Therefore, a multicaster can appear wherever an
<tt CLASS=literal>ActionListener</tt> (or any other listener) is
required. In this case, the <tt CLASS=literal>actionListener</tt>
object will be a multicaster--perhaps not what you expected, and
certainly not what's being passed in the argument
<tt CLASS=literal>l</tt>. Now the code is starting to make sense:
earlier, I said that multicasters maintained linked lists of
listeners. So this method really adds <tt CLASS=literal>l</tt> to
the linked list of action listeners that a multicaster is managing,
and saved the new list.
<P CLASS=para>
But that begs the question: where does the multicaster come from?
The linked list
has to start somewhere. This is where the second trick comes in. 
<tt CLASS=literal>add()</tt> is a static method, so we don't need
a multicaster to call it. But we still need some way to start the
linked list. The class's constructor is never
called--in fact, it's protected, so you can't call it. The answer lies
in the 
<tt CLASS=literal>add()</tt> method, which creates an
<tt CLASS=literal>AWTEventMulticaster</tt> when you need it--that
is, as soon as you add the second listener to the list. The arguments
to <tt CLASS=literal>add()</tt> may be
<tt CLASS=literal>null</tt>; one of them probably is
<tt CLASS=literal>null</tt> when you register your first action
listener. 
<P CLASS=para>
Removing action listeners works the same way. We use the
<tt CLASS=literal>AWTEventMulticaster</tt>'s
<tt CLASS=literal>remove()</tt> method. After the last listener
is taken off the linked list, <tt CLASS=literal>remove()</tt>
returns <tt CLASS=literal>null</tt>.
<P CLASS=para>
With this machinery in place, sending an event to all registered listeners is
very simple. You just create an event by calling its constructor, and
then call the appropriate method in the listener interface to deliver
it. The <tt CLASS=literal>AWTEventMulticaster</tt> makes sure
that the event gets to all the listeners. In this example, we create
an <tt CLASS=literal>ActionEvent</tt> and deliver the event to
the listeners' <tt CLASS=literal>actionPerformed()</tt> methods by
calling
<tt CLASS=literal>actionListener.actionPerformed(event)</tt>. 
<P CLASS=para>
The code to generate other kinds of events is almost exactly the same.
Remember the multicaster implements all the listener interfaces
and has overloaded <tt CLASS=literal>add()</tt> and
<tt CLASS=literal>remove()</tt> methods for every standard
listener type. Therefore, it can be used for any kind of
<tt CLASS=literal>AWTEvent</tt>. It shouldn't be hard to adapt
this example to other situations. 
<P CLASS=para>
What if you want to generate your own event type by subclassing
<tt CLASS=literal>AWTEvent</tt>? To make things concrete, let's
say you want to create an <tt CLASS=literal>ExplosionEvent</tt>
that you generate whenever your monitor catches fire. In this case,
you should define 
your own <tt CLASS=literal>ExplosionListener</tt> interface, and
(possibly) your own <tt CLASS=literal>ExplosionAdapter</tt> class.
You can't use the
<tt CLASS=literal>AWTEventMulticaster</tt> unless your new event
is a subclass of a standard event; extending the multicaster to
support new event types probably isn't worth the effort. It's easier
to write an <tt CLASS=literal>addExplosionListener()</tt> method
that maintains a <tt CLASS=literal>Vector</tt> of listeners and
to deliver events by calling the appropriate method of each listener
in the <tt CLASS=literal>Vector</tt>. We'll demonstrate this
approach in the next section, where we implement another new
component: a <tt CLASS=literal>Dial</tt>. 

<DIV CLASS=sect2>
<h3 CLASS=sect2><A CLASS="TITLE" NAME="ch11-SECT2-AUTOID.6">A Dial Component</A></h3>

<DIV CLASS=screen>
<P>
<PRE>
    Things to mention in widgets Dial event example:
        synchronization issues in add/remove/fire.
        You should sync add/remove... but be wary of syncing fire, 
        deadlocks
</PRE>
</DIV>

<P CLASS=para>
The standard AWT classes don't have a component that's similar to an
old fashioned dial--for example, the volume control on your radio.
Such a component is something of a rarity; I don't remember seeing one
in any application I've used. But there's no reason we can't build
one. In this section, we implement a <tt CLASS=literal>Dial</tt>
class. We also define a new event type,
<tt CLASS=literal>DialEvent</tt>, and a new listener interface,
<tt CLASS=literal>DialListener</tt>. The dial can be used just
like any other Java component. It is built entirely in Java and,
therefore, is a lightweight component; it extends
<tt CLASS=literal>Component</tt> directly and doesn't
have a peer.

<P CLASS=para>
By defining a new event type, I'm stretching the point slightly.
There's no reason our dial couldn't use the standard
<tt CLASS=literal>AdjustmentEvent</tt>. However, this gives us a
chance to show how to handle event listeners without using the event
multicaster. There are many situations in which defining a new event type
will be the only appropriate solution. 
<P CLASS=para>
<A HREF="ch11_09.htm#EXJ-CH-11-FIG-11">Figure 11.11</A> shows what the dial looks like; it is
followed by the code. 

<DIV CLASS=figure>
<h4 CLASS=figure><A CLASS="TITLE" NAME="EXJ-CH-11-FIG-11">Figure 11.11: The Dial Applet</A></h4>


<p>
<img align=middle src="./figs/je1111.gif" alt="[Graphic: Figure 11-11]" width=503 height=128 border=0>

</DIV>

<DIV CLASS=screen>
<P>
<PRE>
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public interface DialListener {
    void dialAdjusted( DialEvent e );
}
public class DialEvent extends AWTEvent {
    int value;
    public static final int DIAL_ADJUSTED = RESERVED_ID_MAX + 1;
    DialEvent( Dial source, int id, int value ) {
        super( source, id );
        this.value = value;
    }
    public int getValue() {
        return value;
    }
}
public class Dial extends Component { 
    int minValue = 0, value, maxValue = 100, radius;
    Vector dialListeners;
    Dial( int maxValue ) {
        this.maxValue = maxValue; 
        enableEvents( AWTEvent.MOUSE_MOTION_EVENT_MASK );
    }
    public void paint( Graphics g ) {
        int tick = 10;
        radius = getSize().width/2 - tick;
        g.drawLine(radius*2+tick/2, radius, radius*2+tick, radius); // the tick
        draw3DCircle( g, 0, 0, radius, true );
        draw3DCircle( g, 1, 1, radius-1, true );
        int knobRadius = radius/7;
        double th = value*(2*Math.PI)/(maxValue-minValue);
        int x = (int)(Math.cos(th)*(radius-knobRadius*3)), 
            y = (int)(Math.sin(th)*(radius-knobRadius*3));
        draw3DCircle( g, x+radius-knobRadius, y+radius-knobRadius, knobRadius, false );
    }
    private void draw3DCircle( Graphics g, int x, int y, int radius, boolean raised ) {
        g.setColor( raised ? Color.white : Color.black );
        g.drawArc( x, y, radius*2, radius*2, 45, 180);
        g.setColor( raised ? Color.black : Color.white);
        g.drawArc( x, y, radius*2, radius*2, 225, 180);
    }
    public void processEvent( AWTEvent e ) {
        if ( e.getID() == MouseEvent.MOUSE_DRAGGED ) {
            int y=((MouseEvent)e).getY();
            int x=((MouseEvent)e).getX();
            double th = Math.atan( (1.0*y-radius)/(x-radius) );
            int value = ( (int)(th/(2*Math.PI) * (maxValue-minValue)) );
            if ( x &lt; radius ) 
                setValue( value + maxValue/2 );
            else if ( y &lt; radius )
                setValue( value + maxValue );
            else 
                setValue( value );
            fireEvent();
        }
        super.processEvent( e );
    }
    public void setValue(int value) { 
        this.value = value; 
        repaint(); 
    }
    public int getValue()  { return value; }
    public void setMinimum(int minValue )  { this.minValue = this.minValue; }
    public int getMinimum()  { return minValue; }
    public void setMaximum(int maxValue )  { this.maxValue = maxValue; }
    public int getMaximum()  { return maxValue; }
    public void addDialListener(DialListener listener) {
        if ( dialListeners == null )
            dialListeners = new Vector();
        dialListeners.addElement( listener );
    }
    public void removeDialListener(DialListener listener) {
        if ( dialListeners != null )
            dialListeners.removeElement( listener );
    }
    private void fireEvent() {
        if ( dialListeners == null )
            return;
        DialEvent event = new DialEvent(this, DialEvent.DIAL_ADJUSTED, value);
        for (Enumeration e = dialListeners.elements(); e.hasMoreElements(); ) 
            ((DialListener)e.nextElement()).dialAdjusted( event );
    }
}    
public class DialApplet extends java.applet.Applet 
                                implements DialListener, AdjustmentListener { 
    final int max = 100;
    Scrollbar scrollbar = new Scrollbar( Scrollbar.HORIZONTAL, 0, 1, 0, max );
    Dial dial = new Dial( max );
    public void init() {
        setLayout( new BorderLayout() );
        dial.addDialListener( this );
        add( "Center", dial );
        scrollbar.addAdjustmentListener( this );
        add( "South", scrollbar );
    }
    public void dialAdjusted( DialEvent e ) {
        scrollbar.setValue( e.getValue() );
    }
    public void adjustmentValueChanged( AdjustmentEvent e ) {
        dial.setValue( e.getValue() );
    }
}
</PRE>
</DIV>

<P CLASS=para>
Let's start from the top. We'll focus on the event handling and leave
you to figure out the trigonometry on your own.  The
<tt CLASS=literal>DialListener</tt> 
interface contains a single method,
<tt CLASS=literal>dialAdjusted()</tt>, which is called when a
<tt CLASS=literal>DialEvent</tt> occurs. The
<tt CLASS=literal>DialEvent</tt> itself is simple. It defines a
new event ID, <tt CLASS=literal>DIAL_ADJUSTED</tt>, that
identifies dial events. This constant is defined so that it won't
conflict with the ID numbers reserved for standard AWT events. The
event itself only carries one item of data: the dial's new value. It
has a single method that returns this value.

<P CLASS=para>
The constructor for the <tt CLASS=literal>Dial</tt> class stores
the dial's maximum value; its minimum value is 0. It then enables
mouse motion events, which the <tt CLASS=literal>Dial</tt> needs
to tell how it is being manipulated. 

<P CLASS=para>
<tt CLASS=literal>paint()</tt>,
<tt CLASS=literal>draw3DCircle()</tt>, and
<tt CLASS=literal>processEvent()</tt> do a lot of trigonometry to
figure out how to display the dial.
<tt CLASS=literal>draw3DCircle()</tt> is a private helper method
that draws a circle that appears either raised or depressed; we use
this to make the dial look three dimensional.
<tt CLASS=literal>processEvent()</tt> is called whenever any
event occurs within the component's area. We only expect to receive
mouse motion events, because these are the only events we enabled.
<tt CLASS=literal>processEvent()</tt> first checks the event's
ID; if it is <tt CLASS=literal>MOUSE_DRAGGED</tt>, the user has
changed the dial's setting. We respond by computing
a new value for the dial, repainting the dial in its new position and
firing a 
<tt CLASS=literal>DialEvent</tt>. Any other events (in
particular, <tt CLASS=literal>MOUSE_MOVED</tt>) are ignored.
However, we call the superclass's
<tt CLASS=literal>processEvent()</tt> method to make sure that
any other processing needed for this event occurs.

<P CLASS=para>
The next group of methods provide ways to retrieve or change the
dial's current setting, minimum, and maximum values. They are similar
to the methods in the <tt CLASS=literal>Adjustable</tt>
interface; you could argue that <tt CLASS=literal>Dial</tt>
really ought to implement <tt CLASS=literal>Adjustable</tt>. 

<P CLASS=para>
Finally, we reach the methods that work with listeners.
<tt CLASS=literal>addDialListener()</tt> adds a new listener to a
<tt CLASS=literal>Vector</tt> of listeners by calling
<tt CLASS=literal>addElement()</tt>. If the vector doesn't
already exist, <tt CLASS=literal>addDialListener()</tt> creates
it. <tt CLASS=literal>removeDialListener()</tt> simply takes a
listener off the list, so that it won't receive any further events.
fireEvent(<tt CLASS=literal>)</tt> is a private method that
creates a <tt CLASS=literal>DialEvent</tt> and sends it to every
listener. It does so by converting the
<tt CLASS=literal>Vector</tt> into an
<tt CLASS=literal>Enumeration</tt> and running through every
element in the list by calling
<tt CLASS=literal>nextElement()</tt> until
<tt CLASS=literal>hasMoreElements()</tt> returns false. To send
the event to a listener, it calls the listener's
<tt CLASS=literal>dialAdjusted()</tt> method. Note that
<tt CLASS=literal>nextElement()</tt> returns an
<tt CLASS=literal>Object</tt>; we must cast this object to
<tt CLASS=literal>DialListener</tt> before we can deliver the
event. 

<P CLASS=para>
To show how the applet is used, I included a simple applet called
<tt CLASS=literal>DialApplet</tt>. This applet places a
<tt CLASS=literal>Dial</tt> and a
<tt CLASS=literal>Scrollbar</tt> in a border layout. Any change
to either the dial or the scrollbar is reflected by the other. The
applet implements both <tt CLASS=literal>DialListener</tt> and
<tt CLASS=literal>AdjustmentListener</tt>, and therefore has both
<tt CLASS=literal>dialAdjusted()</tt> and
<tt CLASS=literal>adjustmentValueChanged()</tt> methods. Although
this isn't necessarily a good argument for creating a new event type,
it's worth noticing that the logic of the listener methods is much
simpler than it would have been if the dial generated adjustment
events. 

</DIV>

</DIV>


<DIV CLASS=htmlnav>

<P>
<HR align=left width=515>
<table width=515 border=0 cellpadding=0 cellspacing=0>
<tr>
<td width=172 align=left valign=top><A HREF="ch11_08.htm"><IMG SRC="gifs/txtpreva.gif" ALT="Previous" border=0></A></td>
<td width=171 align=center valign=top><a href="index.htm"><img src='gifs/txthome.gif' border=0 alt='Home'></a></td>
<td width=172 align=right valign=top><A HREF="ch12_01.htm"><IMG SRC="gifs/txtnexta.gif" ALT="Next" border=0></A></td>
</tr>
<tr>
<td width=172 align=left valign=top>Dialogs</td>
<td width=171 align=center valign=top><a href="index/idx_0.htm"><img src='gifs/index.gif' alt='Book Index' border=0></a></td>
<td width=172 align=right valign=top>Layout Managers</td>
</tr>
</table>
<hr align=left width=515>

<IMG SRC="gifs/smnavbar.gif" USEMAP="#map" BORDER=0> 
<MAP NAME="map"> 
<AREA SHAPE=RECT COORDS="0,0,108,15" HREF="../javanut/index.htm"
alt="Java in a Nutshell"> 
<AREA SHAPE=RECT COORDS="109,0,200,15" HREF="../langref/index.htm" 
alt="Java Language Reference"> 
<AREA SHAPE=RECT COORDS="203,0,290,15" HREF="../awt/index.htm" 
alt="Java AWT"> 
<AREA SHAPE=RECT COORDS="291,0,419,15" HREF="../fclass/index.htm" 
alt="Java Fundamental Classes"> 
<AREA SHAPE=RECT COORDS="421,0,514,15" HREF="../exp/index.htm" 
alt="Exploring Java"> 
</MAP>
</DIV>

</BODY>
</HTML>
